package service

import (
	"MyBlogv2/blog-common/HttpCode"
	define2 "MyBlogv2/blog-common/define"
	"MyBlogv2/blog-common/helper"
	"MyBlogv2/blog-common/models"
	"MyBlogv2/blog-common/oss"
	"MyBlogv2/blog-common/redisUtil"
	"MyBlogv2/blog-common/response"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"math"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func GetRecommendList(c *gin.Context) {
	var articleVo []models.RecommendArticleVo
	var article []models.ArticleVo
	models.Db.Model(new(models.ArticleVo)).
		Where("is_top = ?", define2.RECOMMEND_ARTICLE).
		Where("status = ?", define2.PUBLIC_ARTICLE).
		Find(&article)
	copier.Copy(&articleVo, &article)
	response.ResponseOK(c, articleVo)
	return

}

func GetArticleListByTypeId(c *gin.Context) {
	typeId, _ := strconv.Atoi(c.Param("typeId"))
	typeOf, _ := strconv.Atoi(c.Query("type"))
	articleVo := []models.ArticleVo{}
	if typeOf == 1 {
		models.Db.Model(new(models.ArticleVo)).Where("category_id = ?", typeId).Find(&articleVo)
	} else if typeOf == 2 {
		models.Db.Model(new(models.ArticleVo)).
			Distinct("a.id").
			Table("t_article a").
			Select("a.*").
			Joins("left join t_article_tag at on at.article_id = a.id").
			Where("at.tag_id = ?", typeId).
			Find(&articleVo)
	}
	response.ResponseOK(c, articleVo)
	return
}

func GetArticlePageList(c *gin.Context) {
	pageNum, _ := strconv.Atoi(c.DefaultQuery("pageNum", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	var articleVo []models.ArticleVo
	var total int64
	models.Db.Model(new(models.ArticleVo)).
		Table("t_article a").
		Select("a.*,c.category_name as category_name").
		Joins("left join t_category c on a.category_id = c.id").
		Where("a.status = ?", define2.PUBLIC_ARTICLE).
		Count(&total).
		Order("a.created_at desc").
		Offset((pageNum - 1) * pageSize).
		Limit(pageSize).
		Find(&articleVo)
	for i, v := range articleVo {
		var tags []string
		models.Db.Model(new(models.ArticleTag)).
			Table("t_article_tag at").
			Select("t.tag_name").
			Joins("left join t_tag t on at.tag_id = t.id").
			Where("at.article_id = ?", v.Id).
			Find(&tags)
		articleVo[i].Tags = tags
	}
	response.ResponseOK(c, gin.H{
		"page":  articleVo,
		"total": total,
	})
	return

}

func GetArticleDetailById(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var articleVo models.ArticleVo
	var articleDetailVo models.ArticleDetailVo
	models.Db.Model(new(models.ArticleVo)).
		Table("t_article a").
		Select("a.*,c.category_name as category_name").
		Joins("left join t_category c on a.category_id = c.id").
		Where("a.id = ?", id).
		Where("a.status = ?", define2.PUBLIC_ARTICLE).
		First(&articleVo)
	var tagsVo []models.TagVo
	var tags []models.Tag
	models.Db.Model(new(models.Tag)).
		Table("t_tag t").
		Joins("left join t_article_tag at on at.tag_id = t.id").
		Where("at.article_id = ?", id).
		Distinct("t.tag_name").
		Find(&tags)
	copier.Copy(&tagsVo, &tags)
	copier.Copy(&articleDetailVo, &articleVo)
	articleDetailVo.Tags = tagsVo
	var nextArticle models.Article
	var preArticle models.Article
	models.Db.Model(new(models.Article)).Where("id > ?", id).Order("id asc").First(&nextArticle)
	models.Db.Model(new(models.Article)).Where("id < ?", id).Order("id desc").First(&preArticle)
	var likeCount int64
	models.Db.Model(new(models.Like)).Where("type_id = ? and type = ?", id, define2.LIKE_TYPE_ARTICLE).Count(&likeCount)
	var favoriteCount int64
	models.Db.Model(new(models.Favorite)).Where("type_id = ? and type = ?", id, define2.LIKE_TYPE_ARTICLE).Count(&favoriteCount)
	var commentCount int64
	models.Db.Model(new(models.Comment)).Where("type_id = ? and type = ?", id, define2.LIKE_TYPE_ARTICLE).Count(&commentCount)
	articleDetailVo.CommontCount = int(commentCount)
	articleDetailVo.FavoriteCount = int(favoriteCount)
	articleDetailVo.LikeCount = int(likeCount)
	articleDetailVo.NextArticleId = nextArticle.Id
	articleDetailVo.PreArticleId = preArticle.Id
	articleDetailVo.NextArticleTitle = nextArticle.ArticleTitle
	articleDetailVo.PreArticleTitle = preArticle.ArticleTitle
	response.ResponseOK(c, articleDetailVo)
	return
}

func UpdateVisitCount(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	redisUtil.AddVisitCount(id)
	response.ResponseOK(c, nil)
	return
}

func Related(c *gin.Context) {
	categoryId, _ := strconv.Atoi(c.Param("categoryId"))
	articleId, _ := strconv.Atoi(c.Param("articleId"))
	var articleVoList []models.ArticleVo
	var relatedArticleVoList []models.RelatedArticleVo
	models.Db.Model(new(models.ArticleVo)).
		Where("status = ?", define2.PUBLIC_ARTICLE).
		Where("category_id = ?", categoryId).
		Where("id != ?", articleId).
		Limit(define2.RELATED_ARTICLE_COUNT).
		Find(&articleVoList)

	copier.Copy(&relatedArticleVoList, &articleVoList)
	response.ResponseOK(c, relatedArticleVoList)
	return
}

func GetRandomArticleList(c *gin.Context) {
	var article []models.ArticleVo
	models.Db.Model(new(models.ArticleVo)).
		Where("status = ?", define2.PUBLIC_ARTICLE).
		Order("rand()").
		Limit(define2.RANDOM_ARTICLE_COUNT).
		Find(&article)
	response.ResponseOK(c, article)
	return
}

func InitSearchByTitle(c *gin.Context) {
	var articleVo []models.ArticleVo
	var initSearchTitleVo []models.InitSearchTitleVo
	models.Db.Model(new(models.ArticleVo)).
		Table("t_article a").
		Select("a.*,c.category_name as category_name").
		Joins("left join t_category c on a.category_id = c.id").
		Where("a.status = ?", define2.PUBLIC_ARTICLE).
		Find(&articleVo)
	copier.Copy(&initSearchTitleVo, &articleVo)
	response.ResponseOK(c, initSearchTitleVo)
	return
}

func SearchArticleByContent(c *gin.Context) {
	var articleVo []models.ArticleVo
	var searchArticleByContentVo []models.SearchArticleByContentVo
	keyword := c.Query("content")
	if keyword != "" {
		models.Db.Model(new(models.ArticleVo)).
			Table("t_article a").
			Select("a.*,c.category_name as category_name").
			Joins("left join t_category c on a.category_id = c.id").
			Where("a.status = ?", define2.PUBLIC_ARTICLE).
			Where("a.article_content like ?", "%"+keyword+"%").
			Find(&articleVo)
		copier.Copy(&searchArticleByContentVo, &articleVo)
	} else {
		models.Db.Model(new(models.ArticleVo)).
			Table("t_article a").
			Select("a.*,c.category_name as category_name").
			Joins("left join t_category c on a.category_id = c.id").
			Where("a.status = ?", define2.PUBLIC_ARTICLE).
			Find(&articleVo)
		copier.Copy(&searchArticleByContentVo, &articleVo)
	}
	if len(searchArticleByContentVo) != 0 {
		index := -1
		for i, v := range searchArticleByContentVo {
			index = -1
			content := v.ArticleContent
			index = strings.Index(strings.ToLower(content), strings.ToLower(keyword))
			if index != -1 {
				end := math.Min(float64(len(content)), float64(index+len(keyword)+50))

				highlighted := keyword + string(content[index+len(keyword):int(end)])
				searchArticleByContentVo[i].ArticleContent = strings.TrimRight(stripMarkdown(highlighted), "�")
			}
		}
		response.ResponseOK(c, searchArticleByContentVo)
		return
	}
	response.ResponseOK(c, new([]models.SearchArticleByContentVo))
	return

}

func GetTimeLineList(c *gin.Context) {
	var articleVo []models.ArticleVo
	var articleTimeLineVo []models.TimeLineVo
	models.Db.Model(new(models.ArticleVo)).Find(&articleVo)
	copier.Copy(&articleTimeLineVo, &articleVo)
	response.ResponseOK(c, articleTimeLineVo)
	return
}
func UploadArticleCover(c *gin.Context) {
	file, _ := c.FormFile("articleCover")
	filename := file.Filename
	ext := path.Ext(filename)
	objectName := helper.GetUUID() + ext
	filePath, err := file.Open()
	if err != nil {
		response.ResponseError(c, HttpCode.FILE_UPLOAD_ERROR)
		return
	}
	uploadFilePath, err := oss.UploadFile(objectName, filePath)
	if err != nil {
		response.ResponseError(c, HttpCode.FILE_UPLOAD_ERROR)
		return
	}
	response.ResponseOK(c, uploadFilePath)
	return

}

func PublishArticle(c *gin.Context) {
	var articleDto models.ArticleDto
	if err := c.ShouldBindJSON(&articleDto); err != nil {
		response.ResponseError(c, HttpCode.PARAM_ERROR)
		return
	}
	var article models.Article
	userId, _ := c.Get("userId")
	article.UserId = userId.(int)
	copier.Copy(&article, &articleDto)

	var articleSearch models.ArticleEs
	var categoryName string
	var tagsName []string
	models.Db.Model(new(models.Category)).Select("category_name").Where("id = ?", article.CategoryId).Find(&categoryName)
	models.Db.Model(new(models.Tag)).Select("tag_name").Where("id in (?)", articleDto.TagId).Find(&tagsName)
	copier.Copy(&articleSearch, &articleDto)
	articleSearch.TagsName = tagsName
	articleSearch.CategoryName = categoryName
	articleSearch.CreateTime = models.MyTime(time.Now())

	if article.Id == 0 {
		models.Db.Model(new(models.Article)).Create(&article)
		//es.SaveArticle(articleSearch, article.Id)
	} else {
		models.Db.Model(new(models.Article)).Where("id = ?", article.Id).Updates(&article)
		//es.UpdateArticle(articleSearch, article.Id)
	}
	sql := "delete from t_article_tag where article_id = ?"
	models.Db.Exec(sql, article.Id)
	for _, v := range articleDto.TagId {
		var articleTag models.ArticleTag
		articleTag.ArticleId = article.Id
		articleTag.TagId = v
		models.Db.Model(new(models.ArticleTag)).Create(&articleTag)
	}
	response.ResponseOK(c, nil)
	return

}

func UploadArticleImage(c *gin.Context) {
	file, _ := c.FormFile("articleImage")
	filename := file.Filename
	ext := path.Ext(filename)
	objectName := helper.GetUUID() + ext
	filePath, err := file.Open()
	if err != nil {
		response.ResponseError(c, HttpCode.FILE_UPLOAD_ERROR)
		return
	}
	uploadFilePath, err := oss.UploadFile(objectName, filePath)
	if err != nil {
		response.ResponseError(c, HttpCode.FILE_UPLOAD_ERROR)
		return
	}
	response.ResponseOK(c, uploadFilePath)
	return

}

func GetArticleList(c *gin.Context) {
	var articleVoList []models.ArticleListVo
	models.Db.Model(new(models.ArticleListVo)).
		Table("t_article a").
		Select("a.*,c.category_name as category_name,u.username as username").
		Joins("left join t_category c on a.category_id = c.id").
		Joins("left join sys_user u on a.user_id = u.id").
		Find(&articleVoList)

	for i, v := range articleVoList {
		var tagsName []string
		models.Db.Model(new(models.ArticleTag)).
			Distinct("t.tag_name").
			Table("t_article_tag at").
			Select("t.tag_name").
			Joins("left join t_tag t on at.tag_id = t.id").
			Where("at.article_id = ?", v.Id).
			Find(&tagsName)
		articleVoList[i].TagsName = tagsName
	}
	response.ResponseOK(c, articleVoList)
	return
}

func SearchArticle(c *gin.Context) {
	var searchArticleDto models.SearchArticleDto
	if err := c.ShouldBindJSON(&searchArticleDto); err != nil {
		response.ResponseError(c, HttpCode.PARAM_ERROR)
		return
	}
	var articleVoList []models.ArticleListVo
	db := models.Db.Model(new(models.ArticleListVo)).
		Table("t_article a").
		Select("a.*,c.category_name as category_name,u.username as username").
		Joins("left join t_category c on a.category_id = c.id").
		Joins("left join sys_user u on a.user_id = u.id")
	if searchArticleDto.ArticleTitle != "" {
		db.Where("a.article_title like ?", "%"+searchArticleDto.ArticleTitle+"%")
	}
	if searchArticleDto.CategoryId != 0 {
		db.Where("a.category_id = ?", searchArticleDto.CategoryId)
	}
	if searchArticleDto.Status.Valid {
		db.Where("a.status = ?", searchArticleDto.Status.Int64)
	}
	if searchArticleDto.IsTop.Valid {
		db.Where("a.is_top = ?", searchArticleDto.IsTop.Int64)
	}
	db.Find(&articleVoList)

	for i, v := range articleVoList {
		var tagsName []string
		models.Db.Model(new(models.ArticleTag)).
			Distinct("t.tag_name").
			Table("t_article_tag at").
			Select("t.tag_name").
			Joins("left join t_tag t on at.tag_id = t.id").
			Where("at.article_id = ?", v.Id).
			Find(&tagsName)
		articleVoList[i].TagsName = tagsName
	}
	response.ResponseOK(c, articleVoList)
	return

}

func UpdateArticleStatus(c *gin.Context) {
	id, _ := strconv.Atoi(c.Query("id"))
	status, _ := strconv.Atoi(c.Query("status"))
	models.Db.Model(new(models.Article)).Where("id = ?", id).Update("status", status)
	response.ResponseOK(c, nil)
	return
}

func UpdateArticleIsTop(c *gin.Context) {
	id, _ := strconv.Atoi(c.Query("id"))
	isTop, _ := strconv.Atoi(c.Query("isTop"))
	models.Db.Model(new(models.Article)).Where("id = ?", id).Update("is_top", isTop)
	response.ResponseOK(c, nil)
	return
}

func GetArticleEcho(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var articleDto models.ArticleDto
	models.Db.Model(new(models.ArticleDto)).Where("id = ?", id).Find(&articleDto)
	var tagId []int
	models.Db.Model(new(models.ArticleTag)).Distinct("tag_id").Select("tag_id").Where("article_id = ?", id).Find(&tagId)
	articleDto.TagId = tagId
	response.ResponseOK(c, articleDto)
	return

}
func GetHotArticleList(c *gin.Context) {
	var hotArticleVoList []models.HotArticleVo
	models.Db.Model(new(models.HotArticleVo)).Order("visit_count desc").Limit(5).Find(&hotArticleVoList)
	response.ResponseOK(c, hotArticleVoList)

}
func DeleteArticle(c *gin.Context) {
	var ids []int
	if err := c.ShouldBindJSON(&ids); err != nil {
		response.ResponseError(c, HttpCode.PARAM_ERROR)
		return
	}
	models.Db.Model(new(models.Article)).Where("id in (?)", ids).Delete(new(models.Article))
	//es.DeleteArticle(ids)
	response.ResponseOK(c, nil)
	return

}

func DeleteArticleCover(c *gin.Context) {
	response.ResponseOK(c, nil)
	return
}

func stripMarkdown(markdown string) string {
	// 去掉标题
	re := regexp.MustCompile(`(?m)^\s*#.*$`)
	markdown = re.ReplaceAllString(markdown, "")

	// 去掉加粗
	re = regexp.MustCompile(`\*\*(.*?)\*\*`)
	markdown = re.ReplaceAllString(markdown, "$1")

	// 去掉斜体
	re = regexp.MustCompile(`\*(.*?)\*`)
	markdown = re.ReplaceAllString(markdown, "$1")

	// 去掉行内代码
	re = regexp.MustCompile("`([^`]*)`")
	markdown = re.ReplaceAllString(markdown, "$1")

	// 去掉删除线
	re = regexp.MustCompile(`~~(.*?)~~`)
	markdown = re.ReplaceAllString(markdown, "$1")

	// 去掉链接
	re = regexp.MustCompile(`\[(.*?)\]\(.*?\)`)
	markdown = re.ReplaceAllString(markdown, "$1")

	// 去掉图片
	re = regexp.MustCompile(`!\[.*?\]\(.*?\)`)
	markdown = re.ReplaceAllString(markdown, "")

	// 去掉引用
	re = regexp.MustCompile(`>\s?`)
	markdown = re.ReplaceAllString(markdown, "")

	// 去掉无序列表
	re = regexp.MustCompile(`(?m)^\s*[-*+]\s+`)
	markdown = re.ReplaceAllString(markdown, "")

	// 去掉有序列表
	re = regexp.MustCompile(`(?m)^\s*\d+\.\s+`)
	markdown = re.ReplaceAllString(markdown, "")

	// 去掉换行符
	markdown = strings.ReplaceAll(markdown, "\n", " ")

	return markdown
}
